require 'MVVM.Sources.View.Adaptor'

ListAdaptorA = class('ListAdaptor', Adaptor)

function ListAdaptorA:ctor(gameObject, propertyName, prefab, binder, doAnimation,modifyFunc)--这里不管理prefab释放，谁使用这个谁管理prefab的释放
    self.prefab = prefab
    self.doAnimationOnInit = false
    self.doAnimation = doAnimation
    self.removeAnimation = nil
    self.modifyFunc = modifyFunc
    self.bindManually = false
    self.component = gameObject:GetComponent('RectTransform')
    self.gameObjects = {}
    self.opQueue = {}
    self:BindProperty(propertyName, binder)
    binder:AddEx(propertyName,
            function(viewModel) self:OnAdd(viewModel) end,
            function(index, viewModel) self:OnInsert(index, viewModel) end,
            function(index) self:OnRemove(index) end)

    self.view:StartCoroutineA(function()
        while true do
            if #self.opQueue > 0 then
                local curOp = self.opQueue[1]
                table.remove(self.opQueue, 1)
                curOp()
            else
                coroutine.step()
            end
        end
    end)
end

function ListAdaptorA:GetPrefab(viewModel)
    if type(self.prefab) == 'function' then
        return self.prefab(viewModel)
    else
        return self.prefab
    end
end

function ListAdaptorA:OnViewModelValueChanged(oldValue, newValue)
    if newValue == nil then
        return
    end

    local copyList = {}
    for _, item in ipairs(newValue) do
        table.insert(copyList, item)
    end

    for _, go in ipairs(self.gameObjects) do
        go.transform:SetParent(nil)
        GameObject.Destroy(go)
    end
    self.gameObjects = {}

    for i, viewModel in pairs(copyList) do
        local go = GameObject.New()
        local prefab = self:GetPrefab(viewModel)
        local copyLayout = go:AddComponent(typeof(UnityEngine.UI.LayoutElement))
        local layoutElement = prefab:GetComponent('LayoutElement')
        if layoutElement then
            copyLayout.ignoreLayout = layoutElement.ignoreLayout
            copyLayout.minWidth = layoutElement.minWidth
            copyLayout.minHeight = layoutElement.minHeight
            copyLayout.preferredWidth = layoutElement.preferredWidth
            copyLayout.preferredHeight = layoutElement.preferredHeight
            copyLayout.flexibleWidth = layoutElement.flexibleWidth
            copyLayout.flexibleHeight = layoutElement.flexibleHeight
            copyLayout.layoutPriority = layoutElement.layoutPriority
        end
        go.transform:SetParent(self.component, false)
        table.insert(self.gameObjects, go)
    end
    UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(self.component)

    local doInitAsync = function()
        for i, viewModel in pairs(copyList) do
            local stub = self.gameObjects[i]
            GameObject.Destroy(stub)
            local go = GameObject.Instantiate(self:GetPrefab(viewModel))
            go.transform:SetParent(self.component, false)
            go.transform:SetSiblingIndex(i - 1)
            self.gameObjects[i] = go

            local view = GetLuaObject(go)
            if view then
                if self.bindManually then
                    view.bindLaterViewModel = viewModel
                else
                    view:Set_BindingContext(viewModel)
                end

                if self.modifyFunc~=nil then
                    self.modifyFunc(view)
                end

                if self.doAnimationOnInit and self.doAnimation then
                    self.doAnimation(go, i)
                end
            end
            UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(go.transform)
            coroutine.step()
        end
        UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(self.component)
    end

    self:AddOp(doInitAsync)
end

function ListAdaptorA:OnAdd(viewModel)
    local doAddAsync = function()
        local go = GameObject.Instantiate(self:GetPrefab(viewModel))

        UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(go.transform)
        go.transform:SetParent(self.component, false)
        go.transform:SetAsLastSibling()
        table.insert(self.gameObjects, go)
        local view = GetLuaObject(go)
        if view then
            if self.bindManually then
                view.bindLaterViewModel = viewModel
            else
                view:Set_BindingContext(viewModel)
            end

            if self.modifyFunc~=nil then
                self.modifyFunc(view)
            end

            if self.doAnimationOnInit and self.doAnimation then
                self.doAnimation(go, self.component.childCount)
            end
        end

        UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(self.component)
        coroutine.step()
    end

    self:AddOp(doAddAsync)
end

function ListAdaptorA:OnInsert(index, viewModel)
    local doInsertAsync = function()
        local go = GameObject.Instantiate(self:GetPrefab(viewModel))

        UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(go.transform)
        go.transform:SetParent(self.component, false)
        go.transform:SetSiblingIndex(index - 1)
        table.insert(self.gameObjects, index, go)
        local view = GetLuaObject(go)
        if view then
            if self.bindManually then
                view.bindLaterViewModel = viewModel
            else
                view:Set_BindingContext(viewModel)
            end

            if self.modifyFunc~=nil then
                self.modifyFunc(view)
            end
        end

        UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(self.component)
        coroutine.step()
    end

    self:AddOp(doInsertAsync)
end

function ListAdaptorA:OnRemove(index)
    local doRemove = function()
        local trans = self.component.transform:GetChild(index - 1)
        if not utils.IsAvailableGameObject(trans) then
            return
        end
        if self.removeAnimation then
            coroutine.wait(self.removeAnimation(trans.gameObject) or 0.1)
        end
        trans:SetParent(nil)
        GameObject.Destroy(trans.gameObject)
        table.remove(self.gameObjects, index)
        coroutine.step()
    end

    self:AddOp(doRemove)
end

function ListAdaptorA:Resort(targetList,sortedList)
    if #targetList ~= #sortedList then
        return
    end

    local doResort = function()
        local res = {}
        for i = 1, #sortedList do
            local index = table.find(targetList, sortedList[i])
            local rmObj = self.gameObjects[i]
            if index > 0 and index~=i then
                 rmObj = self.gameObjects[index]
            end
            table.insert(res, rmObj)
            rmObj.transform:SetSiblingIndex(i - 1)
        end
        self.gameObjects = res
    end
    self:AddOp(doResort)
end
function ListAdaptorA:AddOp(op)
    table.insert(self.opQueue, op)
end